     
%{
	#ifdef _WIN32
	#include <io.h>
	#endif
	#include <math.h>
	#include <stdio.h>
	#include <QString>
	#include <QByteArray>
    #include "node.h"
	#include "parser_yacc.h"
	#define MAX_STR_CONST = 10000;
%}

%option nounistd
    
%x embed html htmltag comment str str2 script script2 scriptExpr component ctemplate cproperty

    #define SETLLOC parserlloc.first_line = line_num; parserlloc.last_line = line_num

	int html_indent = 0;
	int embed_indent = 0;
    int script_indent = 0;
    int component_indent = 0;
	int is_call = 0;
	int line_num = 1;
	int comment_caller;
	int str_caller;
	int embed_caller;
    int script_caller;
    int component_caller;
    int html_caller;
	QByteArray* str_buffer = 0;
	int str_len = 0;

DIGIT    [0-9]
ID       [a-zA-Z][a-zA-Z0-9_]*
     
%%
     
     
<embed,htmltag,component>\"	{
              str_buffer = new QByteArray();
              str_caller = YY_START;
			  BEGIN(str);
			}
<embed,htmltag,component>\'	{
              str_buffer = new QByteArray();
              str_caller = YY_START;
              BEGIN(str);
            }

<str>\"	{ /* saw closing quote - all done */
          BEGIN(str_caller);
          parserlval.str_buffer = new QString( QString::fromUtf8( str_buffer->constData() ) );
          str_buffer = 0;
          SETLLOC; return T_STRING;
	    }

<str2>\'	{ /* saw closing quote - all done */
          BEGIN(str_caller);
          parserlval.str_buffer = new QString( QString::fromUtf8( str_buffer->constData() ) );
          str_buffer = 0;
          SETLLOC; return T_STRING;
        }

<str,str2>\n { /* Unterminated string */
          SETLLOC; return T_ERROR;
		}
     
<str,str2>\\[0-7]{1,3} { /* octal escape sequence */
                    int result;     
                    (void) sscanf( yytext + 1, "%o", &result );
                    if ( result > 0xff ) {
                         /* error, constant is out-of-bounds */
						yyterminate();
                        SETLLOC; return T_ERROR;
					}
                    str_buffer->append( (char)result );
                 }
     
<str,str2>\\[0-9]+ {
                 /* generate error - bad escape sequence; something
                  * like '\48' or '\0777777'
                  */
                 SETLLOC; return T_ERROR;
              }
     
<str,str2>\\n  str_buffer->append( '\n' );
<str,str2>\\t  str_buffer->append( '\t' );
<str,str2>\\r  str_buffer->append( '\r' );
<str,str2>\\b  str_buffer->append( '\b' );
<str,str2>\\f  str_buffer->append( '\f' );
<str,str2>\\.  str_buffer->append( yytext[1] );
<str,str2>\\\n { line_num++; str_buffer->append( yytext[1] ); }
<str>[^\\\n\"]+ {
                 char *yptr = yytext;     
                 while ( *yptr )
                     str_buffer->append( *yptr++ );
                }
<str2>[^\\\n\']+ {
                 char *yptr = yytext;
                 while ( *yptr )
                     str_buffer->append( *yptr++ );
                 }

<INITIAL,embed,html,component>"<!--"  {
                                comment_caller = YY_START;
                                BEGIN(comment);
                            }
     
<comment>[^-\n]*        /* eat anything that's not a '-' or a new line */
<comment>"-"+"\n"             ++line_num;
<comment>\n             ++line_num;
<comment>"-"[^-\n]*   /* eat up '-' not followed by '-' */
<comment>"--"[^>\n]*   /* eat up '--' not followed by '>' */
<comment>"-->"        BEGIN(comment_caller);

<INITIAL>"template" { SETLLOC; return T_TEMPLATE; }
<INITIAL>"function" { script_caller = YY_START; SETLLOC; BEGIN(script); return T_FUNCTION; }
<INITIAL>"component" { SETLLOC; component_caller = YY_START; BEGIN(component); component_indent = 0; return T_COMPONENT; }
<INITIAL>"var" { embed_caller = YY_START; BEGIN(embed); SETLLOC; return T_VAR; }
<INITIAL>"("|")"|"," { SETLLOC; return yytext[0]; }
<INITIAL>"{"[ \t\n]* {
               line_num += QString(yytext).count('\n');
               html_caller = INITIAL;
			   BEGIN(html);
			   html_indent = 1;
			   is_call = 0;
               SETLLOC; return '{';
		     }
<INITIAL>"$"|"#" { SETLLOC; return yytext[0]; }

<component>"{" { SETLLOC; component_indent++; return yytext[0]; }
<component>"}" { SETLLOC; component_indent--; if ( component_indent == 0 ) BEGIN(component_caller); return yytext[0]; }
<component>":"|"("|")"|","|";"|"="|"$"|"#" { SETLLOC; return yytext[0]; }
<component>"{{" { embed_caller = YY_START; BEGIN(embed); SETLLOC; return T_EMBED_START; }
<component>"event" { SETLLOC; return T_EVENT; }
<component>"var" { embed_caller = YY_START; BEGIN(embed); SETLLOC; return T_VAR; }
<component>"property" { SETLLOC; BEGIN(cproperty); return T_PROPERTY; }
<component>"function" { SETLLOC; script_caller = YY_START; BEGIN(script); return T_FUNCTION; }
<component>"template" { SETLLOC; BEGIN(ctemplate); return T_TEMPLATE; }

<ctemplate>"("|")"|","|"$"|"#" { SETLLOC; return yytext[0]; }
<ctemplate>"{" { SETLLOC; html_caller = component; BEGIN(html); return '{'; }
<ctemplate>";" { SETLLOC; BEGIN(component); return ';'; }

<cproperty>"$"|"#"|"{" { SETLLOC; return yytext[0]; }
<cproperty>"}" { SETLLOC; BEGIN(component); return '}'; }
<cproperty>";" { SETLLOC; BEGIN(component); return ';'; }
<cproperty>"=" { SETLLOC; embed_caller = component; BEGIN(embed); return '='; }
<cproperty>"update" { SETLLOC; script_caller = YY_START; BEGIN(script); return T_UPDATE; }
<cproperty>"readonly" { SETLLOC; return T_READONLY; }

<embed>"and" { SETLLOC; return T_AND; }
<embed>"or" { SETLLOC; return T_OR; }
<embed>"div" { SETLLOC; return T_DIV; }
<embed>"in" { SETLLOC; return T_IN; }
<embed>".." { SETLLOC; return T_DOTDOT; }
     
<embed>{DIGIT}+    { parserlval.i = atoi( yytext ); SETLLOC; return T_NUMBER; }
     
<embed>{DIGIT}+"."{DIGIT}+	{ parserlval.f = atof( yytext ); SETLLOC; return T_FLOAT; }
<embed>"false" { SETLLOC; return T_FALSE; }
<embed>"true" { SETLLOC; return T_TRUE; }
<embed>"null" { SETLLOC; return T_NULL; }
<embed>"if" { SETLLOC; return T_IF; }
<embed>"else" { SETLLOC; return T_ELSE; }
<embed>"then" { SETLLOC; return T_THEN; }
<embed>"==" { SETLLOC; return T_EQUAL; }
<embed>">=" { SETLLOC; return T_LEQ; }
<embed>"<=" { SETLLOC; return T_GEQ; }
<embed>"!=" { SETLLOC; return T_NEQ; }
<embed>"!"|"+"|"-"|"*"|"/"|"^"|"["|"]"|"$"|"#"|"<"|">"|"|"|","|"="|"." { SETLLOC; return yytext[0]; }
<embed>"(" { embed_indent++; SETLLOC; return '('; }
<embed>")" { embed_indent--; if ( is_call && embed_indent == 0 ) {
                is_call = 0;
				BEGIN(html);
		     }
             SETLLOC; return ')'; }

<embed,INITIAL,htmltag,script,component,ctemplate,cproperty>\n  { line_num++; }
<embed,INITIAL,htmltag,script,component,ctemplate,cproperty>[ \t]+          /* eat up whitespace */
          
<embed,INITIAL,htmltag,script,component,ctemplate,cproperty>{ID} { parserlval.str_buffer = new QString( yytext ); SETLLOC; return T_IDENTIFIER; }

<embed>"}}" {
                BEGIN(embed_caller);
                SETLLOC; return T_EMBED_END;
            }

<embed>";" { /* Can happen at the end of a var statement */
                BEGIN(embed_caller);
                SETLLOC; return ';';
            }

<embed>"{"[ \t\n]*  {  /* Can happen after @foreach or @if, which switch to embedded mode */
                line_num += QString(yytext).count('\n');
                BEGIN(html);
                html_indent++;
                SETLLOC; return '{';
            }
		   
<html>"@if" { 
			  BEGIN(embed);
              SETLLOC; return T_IF;
			}
<html>"@component" {
              component_caller = YY_START;
              component_indent = 0;
              BEGIN(component);
              SETLLOC; return T_COMPONENT;
            }
<html>[ \t\n]*"@else"[ \t\n]* {
                  line_num += QString(yytext).count('\n');
                  SETLLOC; return T_ELSE;
              }
<html>"@foreach" {
				   BEGIN(embed);
                   SETLLOC; return T_FOREACH;
				 }
<html>"@"{ID} {
                BEGIN(embed);
                is_call = 1;
                embed_indent = 0;
                QString tmpstr(yytext);
                parserlval.str_buffer = new QString( tmpstr.mid(1) );
                SETLLOC; return T_CALL;
			   }
<html,htmltag>"{{" {
                         embed_caller = YY_START;
			 BEGIN(embed);
             SETLLOC; return T_EMBED_START;
		   }
<html>"<" {
            BEGIN(htmltag);
            SETLLOC; return yytext[0];
          }

<html>"\\{" { parserlval.str_buffer = new QString( "{" ); SETLLOC; return T_TEXT; }
<html>"\\}" { parserlval.str_buffer = new QString( "}" ); SETLLOC; return T_TEXT; }
<html>"\\@" { parserlval.str_buffer = new QString( "@" ); SETLLOC; return T_TEXT; }
<html>"\\\\" { parserlval.str_buffer = new QString( "\\" ); SETLLOC; return T_TEXT; }
<html>"{"[ \t\n]* { html_indent++; SETLLOC; line_num += QString(yytext).count('\n'); return '{'; }
<html>[ \t\n]*"}" { html_indent--; line_num += QString(yytext).count('\n'); if (html_indent == 0) {
                        BEGIN(html_caller);
                    }
                    SETLLOC; return '}';
                  }
<html>[^\n@}{<\\]+ { parserlval.str_buffer = new QString( yytext ); SETLLOC; return T_TEXT; }
<html>\n    { line_num++; parserlval.str_buffer = new QString( "\n" ); SETLLOC; return T_TEXT; }

<htmltag>"style."{ID} { parserlval.str_buffer = new QString( yytext ); SETLLOC; return T_IDENTIFIER; }
<htmltag>"/"|"="|"$" { SETLLOC; return yytext[0]; }
<htmltag>">" { BEGIN(html); SETLLOC; return yytext[0]; }

<html,htmltag,str,str2,comment,embed,script,script2><<EOF>> { yyterminate(); SETLLOC; return T_ERROR; }

<script>"$"|"#"|","|"("|")"|";"|"~" { SETLLOC; return yytext[0]; }
<script>"{" { SETLLOC; BEGIN(script2); script_indent = 1; return '{'; }
<script2>"{" { script_indent++; parserlval.str_buffer = new QString("{"); SETLLOC; return T_SCRIPT; }
<script2>"}" { script_indent--; if ( script_indent == 0 ) { BEGIN(script_caller); return '}'; } parserlval.str_buffer = new QString("}"); SETLLOC; return T_SCRIPT; }
<script2>[^{}\"\']+ { parserlval.str_buffer = new QString(yytext); line_num += parserlval.str_buffer->count('\n'); SETLLOC; return T_SCRIPT; }
<script2,scriptExpr>\"("\\\""|[^\"])*\" { parserlval.str_buffer = new QString(yytext); line_num += parserlval.str_buffer->count('\n'); SETLLOC; return T_SCRIPT; }
<script2,scriptExpr>\'("\\\'"|[^\'])*\' { parserlval.str_buffer = new QString(yytext); line_num += parserlval.str_buffer->count('\n'); SETLLOC; return T_SCRIPT; }

<scriptExpr>"{" { script_indent++; parserlval.str_buffer = new QString("{"); SETLLOC; return T_SCRIPT; }
<scriptExpr>"}" { script_indent--; if ( script_indent == -1 ) return T_ERROR; parserlval.str_buffer = new QString("}"); SETLLOC; return T_SCRIPT; }
<scriptExpr>";" { SETLLOC; if (script_indent == 0 ) { BEGIN(script_caller); } return ';'; }
<scriptExpr>[^;{}\"\']+ { parserlval.str_buffer = new QString(yytext); line_num += parserlval.str_buffer->count('\n'); SETLLOC; return T_SCRIPT; }

<INITIAL,html,htmltag,str,str2,embed,comment,script,script2,scriptExpr,component,ctemplate,cproperty>.  { qDebug( "Unrecognized character: %s\n", yytext ); yyterminate(); SETLLOC; return T_ERROR; }
     
%%
